/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package p1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

/**
 *
 * @author Derek Ward
 * 
 * Contains an interpretation of the data file
 * 
 */
public class DataFile 
{
    private     File                m_file;
    private     String              m_fileName;
    private     int                 m_N;
    private     ArrayList<String>   m_listOfStrings;
    private     ArrayList<String>   m_stringStringN;
    
    final       int                 N_STATE = 0,
                                    LIST_OF_STRINGS_STATE = 1,
                                    STRING_STRING_N_STATE = 2,
                                    FINISHED = 3;
    
    public DataFile(String dataFileName)
    {
        if ( dataFileName != null )
            m_file = new File(dataFileName);
        else
            throw new NullPointerException("DataFile::DataFile(String) was passed a null argument [dataFileName]!");
        
        m_fileName = dataFileName;
        m_N = 0;
        m_listOfStrings = new ArrayList<String>();
        m_stringStringN = new ArrayList<String>();
    }
    
    public void readDataFile() throws IOException
    {
        validateFileState();
        
        readData();
    }
    
    private void validateFileState() throws IOException
    {
        if ( m_file == null )
            throw new IOException("DataFile::validateFileState() the data file object is null!");
        
        if ( !m_file.exists() )
        {
            throw new FileNotFoundException("DataFile::validateFileState() the file, " + m_fileName + " does not exist!");
        }
        
        if ( !m_file.canRead() )
        {
            throw new IOException("DataFile::validateFileState() the file, " + m_fileName + " is not readable!");
        }
    }

    private void readData() throws IOException 
    {
        int state = N_STATE;
        Scanner scanner = new Scanner(new FileInputStream(m_file));
        
        while ( scanner.hasNext() )
        {
            if ( state == N_STATE )
            {
                if ( scanner.hasNextInt() )
                {
                    m_N = scanner.nextInt();
                    
                    if ( m_N < 0 )
                        throw new IOException("DataFile::readData expected the N integer value to be greater than or equal to zero (0)!");
                    
                    scanner.nextLine();   // consume \n
                    
                    state = LIST_OF_STRINGS_STATE;
                }
                else
                {
                    throw new IOException("DataFile::readData expected the N integer value, but it wasn't there!");
                }
            }
            else if ( state == LIST_OF_STRINGS_STATE )
            {
                readListOfStrings(scanner);
                
                state = STRING_STRING_N_STATE;
            }
            else if ( state == STRING_STRING_N_STATE )
            {
                readStringStringN(scanner);
                
                state = FINISHED;
            }
            else if ( state == FINISHED )
            {
                
            }
        }
        
        scanner.close();
    }
    
    private void readListOfStrings(Scanner s) throws IOException
    {
        String      buffer;
        Scanner     scanner;

        if ( s.hasNextLine() )
        {
            buffer = s.nextLine();
            scanner = new Scanner(buffer);

            for ( int i = 0; i < m_N; i++ )
            {
                if ( !scanner.hasNext() && i < m_N )
                {
                    // no more tokens & i < m_N!
                    throw new IOException("DataFile::readListOfStrings expected " + m_N + " strings in the list-of-strings! i=" + i);
                }
                else if ( scanner.hasNext() && i == m_N )
                {
                    // there are more tokens and we're at the end!!!
                    throw new IOException("DataFile::readListOfStrings expected " + m_N + " strings in the list-of-strings! i=" + i);
                }

                m_listOfStrings.add(scanner.next());
            }
        }
        else
        {
            throw new IOException("DataFile::readListOfStrings expected a line of list-of-strings!");
        }
        
        scanner.close();
    }
    
    
    private void readStringStringN(Scanner s) throws IOException
    {
        if ( s.hasNextLine() )          // need at least one data entry!
        {
            while ( s.hasNextLine() )
            {
                m_stringStringN.add(s.nextLine());
            }
        }
        else
        {
            throw new IOException("DataFile::readStringStringN expected at least one string in the string-string-n section!");
        }
    }
    
    private Edge parseStringStringNLine(String s, HashMap<String, Node> vhm) throws IOException, InvalidEdgeWeightException
    {
        Edge        e;
        String      v1, v2;
        Node        tn1, tn2;
        int         w;
        Scanner     scanner = new Scanner(s);
        
        if (vhm == null)
            throw new NullPointerException("DataFile::parseStringStringNLine a null HashMap was passed!");
        
        if ( scanner.hasNext() )
        {
            v1 = scanner.next();
            
            if ( scanner.hasNext() )
            {
                v2 = scanner.next();
                
                if ( scanner.hasNextInt() )
                {
                    w = scanner.nextInt();
                }
                else
                {
                    throw new IOException("DataFile::parseStringStringNLine expected a final int token in this string-string-n line!");
                }
            }
            else
            {
                throw new IOException("DataFile::parseStringStringNLine expected three (3) tokens on this string-string-n line!");
            }
        }
        else
        {
            throw new IOException("DataFile::parseStringStringNLine expected three (3) tokens on this string-string-n line!");
        }
        
        scanner.close();
        
        tn1 = vhm.get(v1);
        tn2 = vhm.get(v2);
        e = new Edge(tn1, tn2, w);
        tn1.m_connectedEdges.add(e);
        tn2.m_connectedEdges.add(e);
        
        return new Edge(vhm.get(v1), vhm.get(v2), w);
    }
    
    public HashMap<String, Node> createNodeHashMap()
    {
        HashMap<String, Node>       tvhm = new HashMap<String, Node>();
        
        if ( m_listOfStrings.size() > 0 )
        {
            for ( int i = 0; i < m_listOfStrings.size(); i++ )
            {
                tvhm.put(m_listOfStrings.get(i), new Node(m_listOfStrings.get(i)));
            }
        }
        else
        {
            throw new IndexOutOfBoundsException("DataFile::createNodeHashMap() the internal list-of-strings is empty! Has a file been parsed yet?");
        }
        
        return tvhm;
    }
    
    public ArrayList<Edge> createEdgeArrayList(HashMap<String, Node> vhm) throws IOException, InvalidEdgeWeightException
    {
        ArrayList<Edge>         teal = new ArrayList<Edge>();
        
        for ( int i = 0; i < m_stringStringN.size(); i++ )
        {
            teal.add( parseStringStringNLine(m_stringStringN.get(i), vhm) );
        }
        
        return teal;
    }
}
